/*
 * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
 *
 * SPDX-License-Identifier: MulanPSL-2.0
 */
#![allow(
    dead_code,
    mutable_transmutes,
    non_camel_case_types,
    non_snake_case,
    non_upper_case_globals,
    unused_assignments,
    unused_mut,
    unused_variables,
    unused_unsafe,
    clippy::never_loop,
    clippy::if_same_then_else
)]

use crate::common::*;

extern "C" {
    fn chmod(__file: *const libc::c_char, __mode: __mode_t) -> libc::c_int;
    fn umask(__mask: __mode_t) -> __mode_t;
    fn mkdir(__path: *const libc::c_char, __mode: __mode_t) -> libc::c_int;
    fn stat(path:*const libc::c_char,buf:*mut stat) -> libc::c_int;
    fn fstat(filedes:libc::c_int,buf:*mut stat) -> libc::c_int;
    fn fclose(__stream: *mut FILE) -> libc::c_int;
    fn fflush(__stream: *mut FILE) -> libc::c_int;
    fn fdopen(__fd: libc::c_int, __modes: *const libc::c_char) -> *mut FILE;
    fn fprintf(_: *mut FILE, _: *const libc::c_char, _: ...) -> libc::c_int;
    fn snprintf(
        _: *mut libc::c_char,
        _: libc::c_ulong,
        _: *const libc::c_char,
        _: ...
    ) -> libc::c_int;
    fn fputc(__c: libc::c_int, __stream: *mut FILE) -> libc::c_int;
    fn fputs(__s: *const libc::c_char, __stream: *mut FILE) -> libc::c_int;
    fn fwrite(
        _: *const libc::c_void,
        _: libc::c_ulong,
        _: libc::c_ulong,
        _: *mut FILE,
    ) -> libc::c_ulong;
    fn ferror(__stream: *mut FILE) -> libc::c_int;
    fn strtoul(_: *const libc::c_char, _: *mut *mut libc::c_char, _: libc::c_int) -> libc::c_ulong;
    fn malloc(_: libc::c_ulong) -> *mut libc::c_void;
    fn free(_: *mut libc::c_void);
    fn mkdtemp(__template: *mut libc::c_char) -> *mut libc::c_char;
    fn strcmp(_: *const libc::c_char, _: *const libc::c_char) -> libc::c_int;
    fn strncmp(_: *const libc::c_char, _: *const libc::c_char, _: libc::c_ulong) -> libc::c_int;
    fn strerror(_: libc::c_int) -> *mut libc::c_char;
    fn memcpy(_: *mut libc::c_void, _: *const libc::c_void, _: libc::c_ulong) -> *mut libc::c_void;
    fn close(__fd: libc::c_int) -> libc::c_int;
    fn read(__fd: libc::c_int, __buf: *mut libc::c_void, __nbytes: size_t) -> ssize_t;
    fn pwrite(
        __fd: libc::c_int,
        __buf: *const libc::c_void,
        __n: size_t,
        __offset: __off_t,
    ) -> ssize_t;
    fn chown(__file: *const libc::c_char, __owner: __uid_t, __group: __gid_t) -> libc::c_int;
    fn fchown(__fd: libc::c_int, __owner: __uid_t, __group: __gid_t) -> libc::c_int;
    fn unlink(__name: *const libc::c_char) -> libc::c_int;
    fn time(__timer: *mut time_t) -> time_t;
    fn __errno_location() -> *mut libc::c_int;
    fn open(__file: *const libc::c_char, __oflag: libc::c_int, _: ...) -> libc::c_int;
    fn fcntl(__fd: libc::c_int, __cmd: libc::c_int, _: ...) -> libc::c_int;
    fn sudo_gettime_awake_v1(ts: *mut timespec) -> libc::c_int;
    fn sudo_lock_file_v1(fd: libc::c_int, action: libc::c_int) -> bool;
    fn sudo_strtobool_v1(str: *const libc::c_char) -> libc::c_int;
    fn sudo_strtonum(
        _: *const libc::c_char,
        _: libc::c_longlong,
        _: libc::c_longlong,
        _: *mut *const libc::c_char,
    ) -> libc::c_longlong;
    fn sudo_strtoid_v2(str: *const libc::c_char, errstr: *mut *const libc::c_char) -> id_t;
    fn log_warning(flags: libc::c_int, fmt: *const libc::c_char, _: ...) -> bool;
    fn log_warningx(flags: libc::c_int, fmt: *const libc::c_char, _: ...) -> bool;
    static mut sudo_defs_table: [sudo_defs_types; 0];
    fn sudo_strtomode_v1(cp: *const libc::c_char, errstr: *mut *const libc::c_char) -> libc::c_int;
    static mut sudo_printf:
        Option<unsafe extern "C" fn(libc::c_int, *const libc::c_char, ...) -> libc::c_int>;
    fn sudo_debug_enter_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        subsys: libc::c_int,
    );
    fn sudo_debug_exit_bool_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        subsys: libc::c_int,
        ret: bool,
    );
    fn sudo_debug_exit_int_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        subsys: libc::c_int,
        ret: libc::c_int,
    );
    fn sudo_debug_exit_size_t_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        subsys: libc::c_int,
        ret: size_t,
    );
    fn sudo_debug_exit_str_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        subsys: libc::c_int,
        ret: *const libc::c_char,
    );
    fn sudo_debug_printf2_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        level: libc::c_int,
        fmt: *const libc::c_char,
        _: ...
    );
    static mut sudoers_subsystem_ids: [libc::c_uint; 0];
    fn bindtextdomain(
        __domainname: *const libc::c_char,
        __dirname: *const libc::c_char,
    ) -> *mut libc::c_char;
    fn sudo_strlcat(dst: *mut libc::c_char, src: *const libc::c_char, siz: size_t) -> size_t;
    fn sudo_strlcpy(dst: *mut libc::c_char, src: *const libc::c_char, siz: size_t) -> size_t;
    fn sudo_sig2str(signo: libc::c_int, signame: *mut libc::c_char) -> libc::c_int;
    fn sudo_warn_gettext_v1(
        domainname: *const libc::c_char,
        msgid: *const libc::c_char,
    ) -> *mut libc::c_char;
    fn sudo_warn_nodebug_v1(fmt: *const libc::c_char, _: ...);
    fn sudo_warnx_nodebug_v1(fmt: *const libc::c_char, _: ...);
    fn sudo_pw_delref(_: *mut passwd);
    fn sudo_freepwcache();
    fn sudo_freegrcache();
    fn sudo_getpwuid(_: uid_t) -> *mut passwd;
    fn sudo_getpwnam(_: *const libc::c_char) -> *mut passwd;
    fn sudo_fakepwnam(_: *const libc::c_char, _: gid_t) -> *mut passwd;
    fn sudo_fakegrnam(_: *const libc::c_char) -> *mut group;
    fn sudo_gr_delref(_: *mut group);
    fn sudo_getgrnam(_: *const libc::c_char) -> *mut group;
    fn sudo_getgrgid(_: gid_t) -> *mut group;
    fn restore_perms() -> bool;
    fn set_perms(_: libc::c_int) -> bool;
    static mut sudo_conv: sudo_conv_t;
    fn sudoers_debug_parse_flags(
        debug_files: *mut sudo_conf_debug_file_list,
        entry: *const libc::c_char,
    ) -> bool;
    fn sudoers_debug_register(
        plugin_path: *const libc::c_char,
        debug_files: *mut sudo_conf_debug_file_list,
    ) -> bool;
    fn sudoers_debug_deregister();
    fn sudo_mkdir_parents(
        path: *mut libc::c_char,
        uid: uid_t,
        gid: gid_t,
        mode: mode_t,
        quiet: bool,
    ) -> bool;
    fn gzdopen(fd: libc::c_int, mode: *const libc::c_char) -> gzFile;
    fn gzwrite(file: gzFile, buf: voidpc, len: libc::c_uint) -> libc::c_int;
    fn gzflush(file: gzFile, flush: libc::c_int) -> libc::c_int;
    fn gzclose(file: gzFile) -> libc::c_int;
    fn gzerror(file: gzFile, errnum: *mut libc::c_int) -> *const libc::c_char;
}

pub const SESSID_MAX: libc::c_uint = 2176782336;

pub const S_IWUSR: libc::c_int = 0o200;
pub const S_ISUID: libc::c_int = 0o4000;
pub const S_ISGID: libc::c_int = 0o2000;
pub const S_ISTXT: libc::c_int = 0o001000;

pub const S_IRGRP: libc::c_int = S_IRUSR >> 3;
pub const S_IWGRP: libc::c_int = S_IWUSR >> 3;
pub const S_IROTH: libc::c_int = S_IRGRP >> 3;
pub const S_IWOTH: libc::c_int = S_IWGRP >> 3;

pub const S_IRWXG: libc::c_int = S_IRWXU >> 3;
pub const S_IRWXO: libc::c_int = S_IRWXG >> 3;

pub const O_RDWR: libc::c_int = 0o2;
pub const O_CREAT: libc::c_int = 0o100;

pub const IOFD_STDIN: libc::c_int = 0;
pub const IOFD_STDOUT: libc::c_int = 1;
pub const IOFD_STDERR: libc::c_int = 2;
pub const IOFD_TTYIN: libc::c_int = 3;
pub const IOFD_TTYOUT: libc::c_int = 4;

pub const PERM_IOLOG: libc::c_int = 0x07;

pub const PATH_MAX: usize = 4096;
pub const ENAMETOOLONG: libc::c_int = 36;

pub const SUDO_LOCK: libc::c_int = 1;
pub const EOVERFLOW: libc::c_int = 75;

pub const INT_MAX: libc::c_int = 2147483647;

pub const Z_SYNC_FLUSH: libc::c_int = 2;
pub const Z_OK: libc::c_int = 0;

pub const IOFD_MAX: libc::c_int = 6;

pub const IOFD_TIMING: libc::c_int = 5;

pub const IO_EVENT_STDIN: libc::c_int = 0;
pub const IO_EVENT_STDOUT: libc::c_int = 1;
pub const IO_EVENT_STDERR: libc::c_int = 2;
pub const IO_EVENT_TTYIN: libc::c_int = 3;
pub const IO_EVENT_TTYOUT: libc::c_int = 4;
pub const IO_EVENT_WINSIZE: libc::c_int = 5;
pub const IO_EVENT_SUSPEND: libc::c_int = 7;

pub const SUDO_IO_PLUGIN: libc::c_int = 2;

pub type voidpc = *const libc::c_void;
pub type __off64_t = libc::c_long;
pub type off64_t = __off64_t;
pub type voidp = *mut libc::c_void;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct gzFile_s {
    pub have: libc::c_uint,
    pub next: *mut libc::c_uchar,
    pub pos: off64_t,
}
pub type gzFile = *mut gzFile_s;

pub type sudo_conv_t = Option<
    unsafe extern "C" fn(
        libc::c_int,
        *const sudo_conv_message,
        *mut sudo_conv_reply,
        *mut sudo_conv_callback,
    ) -> libc::c_int,
>;
pub type sudo_printf_t =
    Option<unsafe extern "C" fn(libc::c_int, *const libc::c_char, ...) -> libc::c_int>;
pub type sudo_hook_fn_t = Option<unsafe extern "C" fn() -> libc::c_int>;
#[derive(Copy, Clone)]
#[repr(C)]
pub struct sudo_conv_message {
    pub msg_type: libc::c_int,
    pub timeout: libc::c_int,
    pub msg: *const libc::c_char,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct sudo_conv_reply {
    pub reply: *mut libc::c_char,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct sudo_conv_callback {
    pub version: libc::c_uint,
    pub closure: *mut libc::c_void,
    pub on_suspend: sudo_conv_callback_fn_t,
    pub on_resume: sudo_conv_callback_fn_t,
}
pub type sudo_conv_callback_fn_t =
    Option<unsafe extern "C" fn(libc::c_int, *mut libc::c_void) -> libc::c_int>;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct group {
    pub gr_name: *mut libc::c_char,
    pub gr_passwd: *mut libc::c_char,
    pub gr_gid: __gid_t,
    pub gr_mem: *mut *mut libc::c_char,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub union io_fd {
    pub f: *mut FILE,
    pub g: gzFile,
    pub v: *mut libc::c_void,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct io_log_file {
    pub enabled: bool,
    pub suffix: *const libc::c_char,
    pub fd: io_fd,
}

#[derive(Copy, Clone)]
#[repr(C)]
pub struct iolog_details {
    pub cwd: *const libc::c_char,
    pub tty: *const libc::c_char,
    pub user: *const libc::c_char,
    pub command: *const libc::c_char,
    pub iolog_path: *const libc::c_char,
    pub runas_pw: *mut passwd,
    pub runas_gr: *mut group,
    pub lines: libc::c_int,
    pub cols: libc::c_int,
    pub ignore_iolog_errors: bool,
}

#[derive(Copy, Clone)]
#[repr(C)]
pub struct io_plugin {
    pub type_0: libc::c_uint,
    pub version: libc::c_uint,
    pub open: Option<
        unsafe extern "C" fn(
            libc::c_uint,
            sudo_conv_t,
            sudo_printf_t,
            *const *mut libc::c_char,
            *const *mut libc::c_char,
            *const *mut libc::c_char,
            libc::c_int,
            *const *mut libc::c_char,
            *const *mut libc::c_char,
            *const *mut libc::c_char,
        ) -> libc::c_int,
    >,
    pub close: Option<unsafe extern "C" fn(libc::c_int, libc::c_int) -> ()>,
    pub show_version: Option<unsafe extern "C" fn(libc::c_int) -> libc::c_int>,
    pub log_ttyin: Option<unsafe extern "C" fn(*const libc::c_char, libc::c_uint) -> libc::c_int>,
    pub log_ttyout: Option<unsafe extern "C" fn(*const libc::c_char, libc::c_uint) -> libc::c_int>,
    pub log_stdin: Option<unsafe extern "C" fn(*const libc::c_char, libc::c_uint) -> libc::c_int>,
    pub log_stdout: Option<unsafe extern "C" fn(*const libc::c_char, libc::c_uint) -> libc::c_int>,
    pub log_stderr: Option<unsafe extern "C" fn(*const libc::c_char, libc::c_uint) -> libc::c_int>,
    pub register_hooks: Option<
        unsafe extern "C" fn(
            libc::c_int,
            Option<unsafe extern "C" fn(*mut sudo_hook) -> libc::c_int>,
        ) -> (),
    >,
    pub deregister_hooks: Option<
        unsafe extern "C" fn(
            libc::c_int,
            Option<unsafe extern "C" fn(*mut sudo_hook) -> libc::c_int>,
        ) -> (),
    >,
    pub change_winsize: Option<unsafe extern "C" fn(libc::c_uint, libc::c_uint) -> libc::c_int>,
    pub log_suspend: Option<unsafe extern "C" fn(libc::c_int) -> libc::c_int>,
}

#[derive(Copy, Clone)]
#[repr(C)]
pub struct sudo_hook {
    pub hook_version: libc::c_uint,
    pub hook_type: libc::c_uint,
    pub hook_fn: sudo_hook_fn_t,
    pub closure: *mut libc::c_void,
}

/*
#[macro_export]
macro_rules! S_IFDIR {
    () => {
        0o040000
    };
}
*/

/*
#[macro_export]
macro_rules! __S_IFMT {
    () => {{
        0o170000
    }};
}
*/

/*
#[macro_export]
macro_rules! S_ISDIR {
    ($m:expr) => {
        ((($m) & crate::__S_IFMT!()) == crate::S_IFDIR!())
    };
}
*/

#[macro_export]
macro_rules! ALLPERMS {
    () => {
        (S_ISUID | S_ISGID | S_ISTXT | S_IRWXU | S_IRWXG | S_IRWXO)
    };
}

#[macro_export]
macro_rules! ACCESSPERMS {
    () => {
        (S_IRWXU | S_IRWXG | S_IRWXO)
    };
}

#[macro_export]
macro_rules! _PATH_SUDO_IO_LOGDIR {
    () => {
        (b"/var/log/utsudo-io\0" as *const u8 as *const libc::c_char)
    };
}

#[macro_export]
macro_rules! PACKAGE_VERSION {
    () => {
        (b"1.8.29\0" as *const u8 as *const libc::c_char)
    };
}

#[macro_export]
macro_rules! sudo_timespecsub {
    ($ts1:expr, $ts2:expr, $ts3:expr) => {{
        ($ts3).tv_sec = ($ts1).tv_sec - ($ts2).tv_sec;
        ($ts3).tv_nsec = ($ts1).tv_nsec - ($ts2).tv_nsec;
        while ($ts3).tv_nsec < 0 as libc::c_int as libc::c_long {
            ($ts3).tv_sec -= 1;
            ($ts3).tv_nsec += 1000000000 as libc::c_int as libc::c_long;
        }
    }};
}

static mut io_log_files: [io_log_file; 7] = [
    {
        io_log_file {
            enabled: 0 as libc::c_int != 0,
            suffix: b"/stdin\0" as *const u8 as *const libc::c_char,
            fd: io_fd {
                f: 0 as *const FILE as *mut FILE,
            },
        }
        //init
    },
    {
        io_log_file {
            enabled: 0 as libc::c_int != 0,
            suffix: b"/stdout\0" as *const u8 as *const libc::c_char,
            fd: io_fd {
                f: 0 as *const FILE as *mut FILE,
            },
        }
        //init
    },
    {
        io_log_file {
            enabled: 0 as libc::c_int != 0,
            suffix: b"/stderr\0" as *const u8 as *const libc::c_char,
            fd: io_fd {
                f: 0 as *const FILE as *mut FILE,
            },
        }
        //init
    },
    {
        io_log_file {
            enabled: 0 as libc::c_int != 0,
            suffix: b"/ttyin\0" as *const u8 as *const libc::c_char,
            fd: io_fd {
                f: 0 as *const FILE as *mut FILE,
            },
        }
        //init
    },
    {
        io_log_file {
            enabled: 0 as libc::c_int != 0,
            suffix: b"/ttyout\0" as *const u8 as *const libc::c_char,
            fd: io_fd {
                f: 0 as *const FILE as *mut FILE,
            },
        }
        //init
    },
    {
        io_log_file {
            enabled: 1 as libc::c_int != 0,
            suffix: b"/timing\0" as *const u8 as *const libc::c_char,
            fd: io_fd {
                f: 0 as *const FILE as *mut FILE,
            },
        }
        //init
    },
    {
        io_log_file {
            enabled: 0 as libc::c_int != 0,
            suffix: 0 as *const libc::c_char,
            fd: io_fd {
                f: 0 as *const FILE as *mut FILE,
            },
        }
        //init
    },
];

static mut iolog_details: iolog_details = iolog_details {
    cwd: 0 as *const libc::c_char,
    tty: 0 as *const libc::c_char,
    user: 0 as *const libc::c_char,
    command: 0 as *const libc::c_char,
    iolog_path: 0 as *const libc::c_char,
    runas_pw: 0 as *const passwd as *mut passwd,
    runas_gr: 0 as *const group as *mut group,
    lines: 0,
    cols: 0,
    ignore_iolog_errors: false,
};
static mut iolog_compress: bool = false;
static mut warned: bool = false;
static mut last_time: timespec = timespec {
    tv_sec: 0,
    tv_nsec: 0,
};
static mut sessid_max: libc::c_uint = SESSID_MAX;
static mut iolog_filemode: mode_t = (S_IRUSR | S_IWUSR) as mode_t;
static mut iolog_dirmode: mode_t = S_IRWXU as mode_t;
static mut iolog_gid_set: bool = false;

/* shared with set_perms.c */
pub type gid_t = libc::c_uint;
pub const ROOT_GID: uid_t = 0;

/*
 * Create directory and any parent directories as needed.
 */
#[inline]
fn io_mkdirs(mut path: *mut libc::c_char) -> bool {
    let mut sb: stat = sb_all_arch;
    let mut ok: bool = false;
    let mut uid_changed: bool = false;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        ok = stat(path, &mut sb) == 0;
        if !ok && *__errno_location() == EACCES {
            /* Try again as the I/O log owner (for NFS). */
            if set_perms(PERM_IOLOG) {
                ok = stat(path, &mut sb) == 0;
                if !restore_perms() {
                    ok = false;
                }
            }
        }
        if ok {
            if S_ISDIR!(sb.st_mode) {
                if (sb.st_uid != iolog_uid || sb.st_gid != iolog_gid)
                    && chown(path, iolog_uid, iolog_gid) != 0
                {
                    sudo_debug_printf!(
                        SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO,
                        b"%s: unable to chown %d:%d %s\0" as *const u8 as *const libc::c_char,
                        get_function_name!(),
                        iolog_uid as libc::c_int,
                        iolog_gid as libc::c_int,
                        path
                    );
                }

                /*if sb.st_uid != iolog_uid || sb.st_gid != iolog_gid {
                    if chown(path, iolog_uid, iolog_gid) != 0 {
                        sudo_debug_printf!(
                            SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO,
                            b"%s: unable to chown %d:%d %s\0" as *const u8 as *const libc::c_char,
                            get_function_name!(),
                            iolog_uid as libc::c_int,
                            iolog_gid as libc::c_int,
                            path
                        );
                    }
                }*/

                if sb.st_mode & ALLPERMS!() as libc::c_uint != iolog_dirmode
                    && chmod(path, iolog_dirmode) != 0 as libc::c_int
                {
                    sudo_debug_printf!(
                        SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO,
                        b"%s: unable to chmod 0%o %s\0" as *const u8 as *const libc::c_char,
                        get_function_name!(),
                        iolog_dirmode,
                        path
                    );
                }

                /*if sb.st_mode & ALLPERMS!() as libc::c_uint != iolog_dirmode {
                    if chmod(path, iolog_dirmode) != 0 as libc::c_int {
                        sudo_debug_printf!(
                            SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO,
                            b"%s: unable to chmod 0%o %s\0" as *const u8 as *const libc::c_char,
                            get_function_name!(),
                            iolog_dirmode,
                            path
                        );
                    }
                }*/
            } else {
                sudo_warnx!(
                    b"%s exists but is not a directory (0%o)\0" as *const u8 as *const libc::c_char,
                    path,
                    sb.st_mode
                );
                ok = false;
            }
            debug_return_bool!(ok);
        }

        ok = sudo_mkdir_parents(path, iolog_uid, iolog_gid, iolog_dirmode, true);
        if !ok && *__errno_location() == EACCES {
            /* Try again as the I/O log owner (for NFS). */
            uid_changed = set_perms(PERM_IOLOG);
            ok = sudo_mkdir_parents(
                path,
                -(1 as libc::c_int) as uid_t,
                -(1 as libc::c_int) as gid_t,
                iolog_dirmode,
                false,
            );
        }
        if ok {
            /* Create final path component. */
            sudo_debug_printf!(
                SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
                b"mkdir %s, mode 0%o\0" as *const u8 as *const libc::c_char,
                path,
                iolog_dirmode
            );
            ok = mkdir(path, iolog_dirmode) == 0 as libc::c_int || *__errno_location() == EEXIST;
            if !ok {
                if *__errno_location() == EACCES && !uid_changed {
                    /* Try again as the I/O log owner (for NFS). */
                    uid_changed = set_perms(PERM_IOLOG);
                    ok = mkdir(path, iolog_dirmode) == 0 as libc::c_int
                        || *__errno_location() == EEXIST;
                }
                if !ok {
                    sudo_warn!(
                        b"unable to mkdir %s\0" as *const u8 as *const libc::c_char,
                        path
                    );
                }
            } else if chown(path, iolog_uid, iolog_gid) != 0 {
                sudo_debug_printf!(
                    SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO,
                    b"%s: unable to chown %d:%d %s\0" as *const u8 as *const libc::c_char,
                    get_function_name!(),
                    iolog_uid as libc::c_int,
                    iolog_gid as libc::c_int,
                    path
                );
            }

            /*} else {
                if chown(path, iolog_uid, iolog_gid) != 0 {
                    sudo_debug_printf!(
                        SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO,
                        b"%s: unable to chown %d:%d %s\0" as *const u8 as *const libc::c_char,
                        get_function_name!(),
                        iolog_uid as libc::c_int,
                        iolog_gid as libc::c_int,
                        path
                    );
                }
            }*/
        }
        if uid_changed && !restore_perms() {
            ok = false;
        }

        /*if uid_changed {
            if !restore_perms() {
                ok = false;
            }
        }*/
        debug_return_bool!(ok);
    } //unsafe
}

/*
 * Create temporary directory and any parent directories as needed.
 */
#[inline]
fn io_mkdtemp(mut path: *mut libc::c_char) -> bool {
    let mut ok: bool = false;
    let mut uid_changed: bool = false;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        ok = sudo_mkdir_parents(path, iolog_uid, iolog_gid, iolog_dirmode, true);
        if !ok && *__errno_location() == EACCES {
            /* Try again as the I/O log owner (for NFS). */
            uid_changed = set_perms(PERM_IOLOG);
            ok = sudo_mkdir_parents(
                path,
                -(1 as libc::c_int) as uid_t,
                -(1 as libc::c_int) as gid_t,
                iolog_dirmode,
                false,
            );
        }
        if ok {
            /* Create final path component. */
            sudo_debug_printf!(
                SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
                b"mkdtemp %s\0" as *const u8 as *const libc::c_char,
                path
            );
            /* We cannot retry mkdtemp() so always use PERM_IOLOG */
            if !uid_changed {
                uid_changed = set_perms(PERM_IOLOG);
            }
            if (mkdtemp(path)).is_null() {
                sudo_warn!(
                    b"unable to mkdir %s\0" as *const u8 as *const libc::c_char,
                    path
                );
                ok = false;
            } else if chmod(path, iolog_dirmode) != 0 as libc::c_int {
                sudo_warn!(
                    b"unable to change mode of %s to 0%o\0" as *const u8 as *const libc::c_char,
                    path,
                    iolog_dirmode
                );
            }
        }
        if uid_changed && !restore_perms() {
            ok = false;
        }

        /*if uid_changed {
            if !restore_perms() {
                ok = false;
            }
        }*/
        debug_return_bool!(ok);
    } //unsafe
}

/*
 * Set max session ID (aka sequence number)
 */
#[inline]
fn io_set_max_sessid(mut maxval: *const libc::c_char) -> bool {
    let mut errstr: *const libc::c_char = std::ptr::null::<libc::c_char>();
    let mut value: libc::c_uint = 0;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        value = sudo_strtonum(
            maxval,
            0 as libc::c_int as libc::c_longlong,
            SESSID_MAX as libc::c_longlong,
            &mut errstr,
        ) as libc::c_uint;
        if !errstr.is_null() {
            if *__errno_location() != ERANGE {
                sudo_debug_printf!(
                    SUDO_DEBUG_ERROR | SUDO_DEBUG_LINENO,
                    b"bad maxseq: %s: %s\0" as *const u8 as *const libc::c_char,
                    maxval,
                    errstr
                );
                debug_return_bool!(false);
            }
            /* Out of range, clamp to SESSID_MAX as documented. */
            value = SESSID_MAX;
        }
        sessid_max = value;
        debug_return_bool!(true);
    } //unsafe
}

/*
 * Sudoers callback for maxseq Defaults setting.
 */
#[no_mangle]
pub fn cb_maxseq(mut sd_un: *const sudo_defs_val) -> bool {
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        /* Clamp value to SESSID_MAX as documented. */
        sessid_max = if (*sd_un).uival < SESSID_MAX {
            (*sd_un).uival
        } else {
            SESSID_MAX
        };
        debug_return_bool!(true);
    } //unsafe
}

/*
 * Look up I/O log user-ID from user name.  Sets iolog_uid.
 * Also sets iolog_gid if iolog_group not specified.
 */
fn iolog_set_user(mut name: *const libc::c_char) -> bool {
    let mut pw: *mut passwd = std::ptr::null_mut::<passwd>();
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        if !name.is_null() {
            pw = sudo_getpwnam(name);
            if !pw.is_null() {
                iolog_uid = (*pw).pw_uid;
                if !iolog_gid_set {
                    iolog_gid = (*pw).pw_gid;
                }
                sudo_pw_delref(pw);
            } else {
                log_warningx(
                    SLOG_SEND_MAIL,
                    b"unknown user: %s\0" as *const u8 as *const libc::c_char,
                    name,
                );
            }
        } else {
            /* Reset to default. */
            iolog_uid = ROOT_UID as libc::c_uint;
            if !iolog_gid_set {
                iolog_gid = ROOT_GID;
            }
        }

        debug_return_bool!(true);
    } //unsafe
}

/*
 * Sudoers callback for iolog_user Defaults setting.
 */
#[no_mangle]
pub unsafe extern "C" fn cb_iolog_user(mut sd_un: *const sudo_defs_val) -> bool {
    iolog_set_user((*sd_un).str_0)
}

/*
 * Look up I/O log group-ID from group name.
 * Sets iolog_gid.
 */
fn iolog_set_group(mut name: *const libc::c_char) -> bool {
    let mut gr: *mut group = std::ptr::null_mut::<group>();
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        if !name.is_null() {
            gr = sudo_getgrnam(name);
            if !gr.is_null() {
                iolog_gid = (*gr).gr_gid;
                iolog_gid_set = true;
                sudo_gr_delref(gr);
            } else {
                log_warningx(
                    SLOG_SEND_MAIL,
                    b"unknown group: %s\0" as *const u8 as *const libc::c_char,
                    name,
                );
            }
        } else {
            /* Reset to default. */
            iolog_gid = ROOT_GID;
            iolog_gid_set = false;
        }

        debug_return_bool!(true);
    } //unsafe
}

/*
 * Look up I/O log group-ID from group name.
 */
#[no_mangle]
pub unsafe extern "C" fn cb_iolog_group(mut sd_un: *const sudo_defs_val) -> bool {
    iolog_set_group((*sd_un).str_0)
}

/*
 * Set iolog_filemode and iolog_dirmode.
 */
fn iolog_set_mode(mut mode: mode_t) -> bool {
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        /* I/O log files must be readable and writable by owner. */
        iolog_filemode = (S_IRUSR | S_IWUSR) as mode_t;

        /* Add in group and other read/write if specified. */
        iolog_filemode |= mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) as libc::c_uint;

        /* For directory mode, add execute bits as needed. */
        iolog_dirmode = iolog_filemode | S_IXUSR as libc::c_uint;
        if iolog_dirmode & (S_IROTH | S_IWOTH) as libc::c_uint != 0 {
            iolog_dirmode |= S_IXGRP as libc::c_uint;
        }
        if iolog_dirmode & (S_IROTH | S_IWOTH) as libc::c_uint != 0 {
            iolog_dirmode |= S_IXOTH as libc::c_uint;
        }

        debug_return_bool!(true);
    } //unsafe
}

/*
 * Sudoers callback for iolog_mode Defaults setting.
 */
#[no_mangle]
pub unsafe extern "C" fn cb_iolog_mode(mut sd_un: *const sudo_defs_val) -> bool {
    iolog_set_mode((*sd_un).mode)
}

/*
 * Wrapper for open(2) that retries with PERM_IOLOG if open(2)
 * returns EACCES.
 */
fn io_open(mut path: *const libc::c_char, mut flags: libc::c_int, mut perm: mode_t) -> libc::c_int {
    let mut fd: libc::c_int = 0;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        fd = open(path, flags, perm);
        if fd == -(1 as libc::c_int) && *__errno_location() == EACCES {
            /* Try again as the I/O log owner (for NFS). */
            if set_perms(PERM_IOLOG) {
                fd = open(path, flags, perm);
                if !restore_perms() {
                    /* restore_perms() warns on error. */
                    if fd != -(1 as libc::c_int) {
                        close(fd);
                        fd = -(1 as libc::c_int);
                    }
                }
            }
        }

        debug_return_int!(fd);
    } //unsafe
}

/*
 * Read the on-disk sequence number, set sessid to the next
 * number, and update the on-disk copy.
 * Uses file locking to avoid sequence number collisions.
 */
#[no_mangle]
pub fn io_nextid(
    mut iolog_dir: *mut libc::c_char,
    mut iolog_dir_fallback: *mut libc::c_char,
    mut sessid: *mut libc::c_char,
) -> bool {
    let mut sb: stat = sb_all_arch;
    let mut buf: [libc::c_char; 32] = [0; 32];
    let mut ep: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut i: libc::c_int = 0;
    let mut len: libc::c_int = 0;
    let mut fd: libc::c_int = -(1 as libc::c_int);
    let mut id: libc::c_ulong = 0 as libc::c_int as libc::c_ulong;
    let mut omask: mode_t = 0;
    let mut nread: ssize_t = 0;
    let mut ret: bool = false;
    let mut pathbuf: [libc::c_char; PATH_MAX] = [0; PATH_MAX];
    static mut b36char: [libc::c_char; 37] = unsafe {
        *::core::mem::transmute::<&[u8; 37], &[libc::c_char; 37]>(
            b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\0",
        )
    };
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        /* umask must not be more restrictive than the file modes. */
        omask = umask(ACCESSPERMS!() as libc::c_uint & !(iolog_filemode | iolog_dirmode));

        'done: loop {
            /*
             * Create I/O log directory if it doesn't already exist.
             */
            if !io_mkdirs(iolog_dir) {
                break 'done;
            }

            /*
             * Open sequence file
             */
            len = snprintf(
                pathbuf.as_mut_ptr(),
                ::core::mem::size_of::<[libc::c_char; PATH_MAX]>() as libc::c_ulong,
                b"%s/seq\0" as *const u8 as *const libc::c_char,
                iolog_dir,
            );
            if len < 0 as libc::c_int
                || len as libc::c_long
                    >= ::core::mem::size_of::<[libc::c_char; PATH_MAX]>() as libc::c_ulong
                        as ssize_t
            {
                *__errno_location() = ENAMETOOLONG;
                log_warning(
                    SLOG_SEND_MAIL,
                    b"%s/seq\0" as *const u8 as *const libc::c_char,
                    pathbuf.as_mut_ptr(),
                );
                break 'done;
            }
            fd = io_open(pathbuf.as_mut_ptr(), O_RDWR | O_CREAT, iolog_filemode);
            if fd == -(1 as libc::c_int) {
                log_warning(
                    SLOG_SEND_MAIL,
                    b"unable to open %s\0" as *const u8 as *const libc::c_char,
                    pathbuf.as_mut_ptr(),
                );
                break 'done;
            }
            sudo_lock_file_v1(fd, SUDO_LOCK);
            if fchown(fd, iolog_uid, iolog_gid) != 0 as libc::c_int {
                sudo_debug_printf!(
                    SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO,
                    b"%s: unable to fchown %d:%d %s\0" as *const u8 as *const libc::c_char,
                    get_function_name!(),
                    iolog_uid as libc::c_int,
                    iolog_gid as libc::c_int,
                    pathbuf.as_mut_ptr()
                );
            }

            /*
             * If there is no seq file in iolog_dir and a fallback dir was
             * specified, look for seq in the fallback dir.  This is to work
             * around a bug in sudo 1.8.5 and older where iolog_dir was not
             * expanded before the sequence number was updated.
             */
            if !iolog_dir_fallback.is_null()
                && fstat(fd, &mut sb) == 0 as libc::c_int
                && sb.st_size == 0 as libc::c_int as libc::c_long
            {
                let mut fallback: [libc::c_char; PATH_MAX] = [0; PATH_MAX];

                len = snprintf(
                    fallback.as_mut_ptr(),
                    ::core::mem::size_of::<[libc::c_char; PATH_MAX]>() as libc::c_ulong,
                    b"%s/seq\0" as *const u8 as *const libc::c_char,
                    iolog_dir_fallback,
                );
                if len > 0 as libc::c_int
                    && (len as libc::c_long)
                        < ::core::mem::size_of::<[libc::c_char; PATH_MAX]>() as libc::c_ulong
                            as ssize_t
                {
                    let mut fd2: libc::c_int =
                        io_open(fallback.as_mut_ptr(), O_RDWR | O_CREAT, iolog_filemode);
                    if fd2 != -(1 as libc::c_int) {
                        if fchown(fd2, iolog_uid, iolog_gid) != 0 as libc::c_int {
                            sudo_debug_printf!(
                                SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO,
                                b"%s: unable to fchown %d:%d %s\0" as *const u8
                                    as *const libc::c_char,
                                get_function_name!(),
                                iolog_uid as libc::c_int,
                                iolog_gid as libc::c_int,
                                fallback.as_mut_ptr()
                            );
                        }
                        nread = read(
                            fd2,
                            buf.as_mut_ptr() as *mut libc::c_void,
                            (::core::mem::size_of::<[libc::c_char; 32]>() as libc::c_ulong)
                                .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                        );
                        if nread > 0 as libc::c_int as libc::c_long {
                            if buf[(nread - 1 as libc::c_int as libc::c_long) as usize]
                                as libc::c_int
                                == '\n' as i32
                            {
                                nread -= 1;
                            }
                            buf[nread as usize] = '\0' as i32 as libc::c_char;
                            id = strtoul(buf.as_mut_ptr(), &mut ep, 36);
                            if ep == buf.as_mut_ptr()
                                || *ep as libc::c_int != '\0' as i32
                                || id >= sessid_max as libc::c_ulong
                            {
                                sudo_debug_printf!(
                                    SUDO_DEBUG_ERROR | SUDO_DEBUG_LINENO,
                                    b"%s: bad sequence number: %s\0" as *const u8
                                        as *const libc::c_char,
                                    fallback.as_mut_ptr(),
                                    buf.as_mut_ptr()
                                );
                                id = 0 as libc::c_ulong;
                            }
                        }
                        close(fd2);
                    }
                }
            }

            /* Read current seq number (base 36). */
            if id == 0 as libc::c_ulong {
                nread = read(
                    fd,
                    buf.as_mut_ptr() as *mut libc::c_void,
                    (::core::mem::size_of::<[libc::c_char; 32]>() as libc::c_ulong)
                        .wrapping_sub(1 as libc::c_ulong),
                );
                if nread != 0 as libc::c_long {
                    if nread == -(1 as libc::c_int) as libc::c_long {
                        log_warning(
                            SLOG_SEND_MAIL,
                            b"unable to read %s\0" as *const u8 as *const libc::c_char,
                            pathbuf.as_mut_ptr(),
                        );
                        break 'done;
                    }
                    if buf[(nread - 1 as libc::c_long) as usize] as libc::c_int == '\n' as i32 {
                        nread -= 1;
                    }
                    buf[nread as usize] = '\0' as i32 as libc::c_char;
                    id = strtoul(buf.as_mut_ptr(), &mut ep, 36);
                    if ep == buf.as_mut_ptr()
                        || *ep as libc::c_int != '\0' as i32
                        || id >= sessid_max as libc::c_ulong
                    {
                        sudo_debug_printf!(
                            SUDO_DEBUG_ERROR | SUDO_DEBUG_LINENO,
                            b"%s: bad sequence number: %s\0" as *const u8 as *const libc::c_char,
                            pathbuf.as_mut_ptr(),
                            buf.as_mut_ptr()
                        );
                        id = 0 as libc::c_ulong;
                    }
                }
            }
            id = id.wrapping_add(1);

            /*
             * Convert id to a string and stash in sessid.
             * Note that that least significant digits go at the end of the string.
             */
            i = 5;
            while i >= 0 {
                buf[i as usize] = b36char[id.wrapping_rem(36 as libc::c_ulong) as usize];
                id = id.wrapping_div(36 as libc::c_ulong);
                i -= 1;
            }
            buf[6_usize] = '\n' as i32 as libc::c_char;

            /* Stash id for logging purposes. */
            memcpy(
                sessid as *mut libc::c_void,
                buf.as_mut_ptr() as *const libc::c_void,
                6 as libc::c_ulong,
            );
            *sessid.offset(6_isize) = '\0' as i32 as libc::c_char;

            /* Rewind and overwrite old seq file, including the NUL byte. */
            if pwrite(
                fd,
                buf.as_mut_ptr() as *const libc::c_void,
                7 as size_t,
                0 as __off_t,
            ) != 7 as libc::c_long
            {
                log_warning(
                    SLOG_SEND_MAIL,
                    b"unable to write to %s\0" as *const u8 as *const libc::c_char,
                    pathbuf.as_mut_ptr(),
                );
                warned = true;
                break 'done;
            }
            ret = true;
            break;
        }
        //done:
        umask(omask);
        if fd != -(1 as libc::c_int) {
            close(fd);
        }
        debug_return_bool!(ret);
    } //unsafe
}

/*
 * Copy iolog_path to pathbuf and create the directory and any intermediate
 * directories.  If iolog_path ends in 'XXXXXX', use mkdtemp().
 * Returns SIZE_MAX on error.
 */
fn mkdir_iopath(
    mut iolog_path: *const libc::c_char,
    mut pathbuf: *mut libc::c_char,
    mut pathsize: size_t,
) -> size_t {
    let mut len: size_t = 0;
    let mut ok: bool = false;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        len = sudo_strlcpy(pathbuf, iolog_path, pathsize);
        if len >= pathsize {
            *__errno_location() = ENAMETOOLONG;
            log_warning(
                SLOG_SEND_MAIL,
                b"%s\0" as *const u8 as *const libc::c_char,
                iolog_path,
            );
            debug_return_size_t!(-(1 as libc::c_int) as size_t);
        }

        /*
         * Create path and intermediate subdirs as needed.
         * If path ends in at least 6 Xs (ala POSIX mktemp), use mkdtemp().
         * Sets iolog_gid (if it is not already set) as a side effect.
         */
        if len >= 6 as libc::c_ulong
            && strcmp(
                &*pathbuf.offset(len.wrapping_sub(6 as libc::c_ulong) as isize),
                b"XXXXXX\0" as *const u8 as *const libc::c_char,
            ) == 0 as libc::c_int
        {
            ok = io_mkdtemp(pathbuf);
        } else {
            ok = io_mkdirs(pathbuf);
        }

        if ok as libc::c_int != 0 {
            debug_return_size_t!(len);
        } else {
            debug_return_size_t!(-(1 as libc::c_int) as size_t);
        }
    } //unsafe
}

/*
 * Append suffix to pathbuf after len chars and open the resulting file.
 * Note that the size of pathbuf is assumed to be PATH_MAX.
 * Uses zlib if docompress is true.
 * Stores the open file handle which has the close-on-exec flag set.
 */
fn open_io_fd(
    mut pathbuf: *mut libc::c_char,
    mut len: size_t,
    mut iol: *mut io_log_file,
    mut docompress: bool,
) -> bool {
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        *pathbuf.offset(len as isize) = '\0' as i32 as libc::c_char;
        sudo_strlcat(pathbuf, (*iol).suffix, PATH_MAX as size_t);
        if (*iol).enabled {
            let mut fd: libc::c_int =
                io_open(pathbuf, O_CREAT | O_TRUNC | O_WRONLY, iolog_filemode);
            if fd != -(1 as libc::c_int) {
                if fchown(fd, iolog_uid, iolog_gid) != 0 as libc::c_int {
                    sudo_debug_printf!(
                        SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO,
                        b"%s: unable to fchown %d:%d %s\0" as *const u8 as *const libc::c_char,
                        get_function_name!(),
                        iolog_uid as libc::c_int,
                        iolog_gid as libc::c_int,
                        pathbuf
                    );
                }
                fcntl(fd, F_SETFD, FD_CLOEXEC);
                if docompress {
                    (*iol).fd.g = gzdopen(fd, b"w\0" as *const u8 as *const libc::c_char);
                } else {
                    (*iol).fd.f = fdopen(fd, b"w\0" as *const u8 as *const libc::c_char);
                }
                if ((*iol).fd.v).is_null() {
                    close(fd);
                    fd = -(1 as libc::c_int);
                }
            }
            if fd == -(1 as libc::c_int) {
                log_warning(
                    SLOG_SEND_MAIL,
                    b"unable to create %s\0" as *const u8 as *const libc::c_char,
                    pathbuf,
                );
                debug_return_bool!(false);
            }
        } else {
            /* Remove old log file if we recycled sequence numbers. */
            unlink(pathbuf);
        }

        debug_return_bool!(true);
    } //unsafe
}

/*
 * Pull out I/O log related data from user_info and command_info arrays.
 * Returns true if I/O logging is enabled, else false.
 */
fn iolog_deserialize_info(
    mut details: *mut iolog_details,
    mut user_info: *const *mut libc::c_char,
    mut command_info: *const *mut libc::c_char,
) -> bool {
    let mut runas_uid_str: *const libc::c_char = b"0\0" as *const u8 as *const libc::c_char;
    let mut runas_euid_str: *const libc::c_char = std::ptr::null::<libc::c_char>();
    let mut runas_gid_str: *const libc::c_char = b"0\0" as *const u8 as *const libc::c_char;
    let mut runas_egid_str: *const libc::c_char = std::ptr::null::<libc::c_char>();
    let mut errstr: *const libc::c_char = std::ptr::null::<libc::c_char>();
    let mut idbuf: [libc::c_char; (MAX_UID_T_LEN + 2) as usize] = [0; (MAX_UID_T_LEN + 2) as usize];
    let mut cur: *const *mut libc::c_char = std::ptr::null::<*mut libc::c_char>();
    let mut id: id_t = 0;
    let mut runas_uid: uid_t = 0 as uid_t;
    let mut runas_gid: gid_t = 0 as gid_t;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        (*details).lines = 24;
        (*details).cols = 80;

        cur = user_info;
        while !(*cur).is_null() {
            match **cur as u8 as char {
                'c' => {
                    if strncmp(
                        *cur,
                        b"cols=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 6]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                    {
                        let mut n: libc::c_int = sudo_strtonum(
                            (*cur)
                                .offset(
                                    ::core::mem::size_of::<[libc::c_char; 6]>() as libc::c_ulong
                                        as isize,
                                )
                                .offset(-(1 as libc::c_int as isize)),
                            1 as libc::c_longlong,
                            INT_MAX as libc::c_longlong,
                            std::ptr::null_mut::<*const libc::c_char>(),
                        ) as libc::c_int;
                        if n > 0 {
                            (*details).cols = n;
                        }
                    }
                    if strncmp(
                        *cur,
                        b"cwd=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 5]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                    {
                        (*details).cwd = (*cur)
                            .offset(::core::mem::size_of::<[libc::c_char; 5]>() as libc::c_ulong
                                as isize)
                            .offset(-(1 as libc::c_int as isize));
                    }
                }
                'l' => {
                    if strncmp(
                        *cur,
                        b"lines=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 7]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                    {
                        let mut n_0: libc::c_int = sudo_strtonum(
                            (*cur)
                                .offset(
                                    ::core::mem::size_of::<[libc::c_char; 7]>() as libc::c_ulong
                                        as isize,
                                )
                                .offset(-(1 as libc::c_int as isize)),
                            1 as libc::c_int as libc::c_longlong,
                            INT_MAX as libc::c_longlong,
                            std::ptr::null_mut::<*const libc::c_char>(),
                        ) as libc::c_int;
                        if n_0 > 0 as libc::c_int {
                            (*details).lines = n_0;
                        }
                    }
                }
                't' => {
                    if strncmp(
                        *cur,
                        b"tty=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 5]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                    {
                        (*details).tty = (*cur)
                            .offset(::core::mem::size_of::<[libc::c_char; 5]>() as libc::c_ulong
                                as isize)
                            .offset(-(1 as libc::c_int as isize));
                    }
                }
                'u' => {
                    if strncmp(
                        *cur,
                        b"user=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 6]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                    {
                        (*details).user = (*cur)
                            .offset(::core::mem::size_of::<[libc::c_char; 6]>() as libc::c_ulong
                                as isize)
                            .offset(-(1 as libc::c_int as isize));
                    }
                }
                _ => {}
            }
            cur = cur.offset(1);
        }

        cur = command_info;
        while !(*cur).is_null() {
            match **cur as u8 as char {
                'c' => {
                    if strncmp(
                        *cur,
                        b"command=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 9]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                    {
                        (*details).command = (*cur)
                            .offset(::core::mem::size_of::<[libc::c_char; 9]>() as libc::c_ulong
                                as isize)
                            .offset(-(1 as libc::c_int as isize));
                    }
                }
                'i' => {
                    if strncmp(
                        *cur,
                        b"ignore_iolog_errors=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 21]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                        && sudo_strtobool_v1(
                            (*cur)
                                .offset(::core::mem::size_of::<[libc::c_char; 21]>()
                                    as libc::c_ulong
                                    as isize)
                                .offset(-(1 as libc::c_int as isize)),
                        ) == true as libc::c_int
                    {
                        (*details).ignore_iolog_errors = true;
                    }

                    if strncmp(
                        *cur,
                        b"iolog_path=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 12]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                    {
                        (*details).iolog_path = (*cur)
                            .offset(
                                ::core::mem::size_of::<[libc::c_char; 12]>() as libc::c_ulong
                                    as isize,
                            )
                            .offset(-(1 as libc::c_int as isize));
                    }

                    if strncmp(
                        *cur,
                        b"iolog_stdin=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 13]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                        && sudo_strtobool_v1(
                            (*cur)
                                .offset(::core::mem::size_of::<[libc::c_char; 13]>()
                                    as libc::c_ulong
                                    as isize)
                                .offset(-(1 as libc::c_int as isize)),
                        ) == true as libc::c_int
                    {
                        io_log_files[IOFD_STDIN as usize].enabled = true;
                    }


                    if strncmp(
                        *cur,
                        b"iolog_stdout=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 14]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                        && sudo_strtobool_v1(
                            (*cur)
                                .offset(::core::mem::size_of::<[libc::c_char; 14]>()
                                    as libc::c_ulong
                                    as isize)
                                .offset(-(1 as libc::c_int as isize)),
                        ) == true as libc::c_int
                    {
                        io_log_files[IOFD_STDOUT as usize].enabled = true;
                    }

                    if strncmp(
                        *cur,
                        b"iolog_stderr=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 14]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                        && sudo_strtobool_v1(
                            (*cur)
                                .offset(::core::mem::size_of::<[libc::c_char; 14]>()
                                    as libc::c_ulong
                                    as isize)
                                .offset(-(1 as libc::c_int as isize)),
                        ) == true as libc::c_int
                    {
                        io_log_files[IOFD_STDERR as usize].enabled = true;
                    }

                    if strncmp(
                        *cur,
                        b"iolog_ttyin=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 13]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                        && sudo_strtobool_v1(
                            (*cur)
                                .offset(::core::mem::size_of::<[libc::c_char; 13]>()
                                    as libc::c_ulong
                                    as isize)
                                .offset(-(1 as libc::c_int as isize)),
                        ) == true as libc::c_int
                    {
                        io_log_files[IOFD_TTYIN as usize].enabled = true;
                    }

                    if strncmp(
                        *cur,
                        b"iolog_compress=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 16]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                        && sudo_strtobool_v1(
                            (*cur)
                                .offset(::core::mem::size_of::<[libc::c_char; 16]>()
                                    as libc::c_ulong
                                    as isize)
                                .offset(-(1 as libc::c_int as isize)),
                        ) == true as libc::c_int
                    {
                        iolog_compress = true; /* must be global */
                    }

                    if strncmp(
                        *cur,
                        b"iolog_ttyout=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 14]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                    {
                        if sudo_strtobool_v1(
                            (*cur)
                                .offset(::core::mem::size_of::<[libc::c_char; 14]>()
                                    as libc::c_ulong
                                    as isize)
                                .offset(-(1 as libc::c_int as isize)),
                        ) == true as libc::c_int
                        {
                            io_log_files[IOFD_TTYOUT as usize].enabled = true;
                        }
                    }

                    if strncmp(
                        *cur,
                        b"iolog_compress=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 16]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                        && sudo_strtobool_v1(
                            (*cur)
                                .offset(::core::mem::size_of::<[libc::c_char; 16]>()
                                    as libc::c_ulong
                                    as isize)
                                .offset(-(1 as libc::c_int as isize)),
                        ) == true as libc::c_int
                    {
                        iolog_compress = true; /* must be global */
                    }

                    if strncmp(
                        *cur,
                        b"iolog_mode=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 12]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                    {
                        let mut mode: mode_t = sudo_strtomode_v1(
                            (*cur)
                                .offset(::core::mem::size_of::<[libc::c_char; 12]>()
                                    as libc::c_ulong
                                    as isize)
                                .offset(-(1 as libc::c_int as isize)),
                            &mut errstr,
                        ) as mode_t;
                        if errstr.is_null() {
                            iolog_set_mode(mode);
                        }
                    }
                    if strncmp(
                        *cur,
                        b"iolog_group=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 13]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                    {
                        iolog_set_group(
                            (*cur)
                                .offset(::core::mem::size_of::<[libc::c_char; 13]>()
                                    as libc::c_ulong
                                    as isize)
                                .offset(-(1 as libc::c_int as isize)),
                        );
                    }
                    if strncmp(
                        *cur,
                        b"iolog_user=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 12]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                    {
                        iolog_set_user(
                            (*cur)
                                .offset(::core::mem::size_of::<[libc::c_char; 12]>()
                                    as libc::c_ulong
                                    as isize)
                                .offset(-(1 as libc::c_int as isize)),
                        );
                    }
                }
                'm' => {
                    if strncmp(
                        *cur,
                        b"maxseq=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 8]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                    {
                        io_set_max_sessid(
                            (*cur)
                                .offset(
                                    ::core::mem::size_of::<[libc::c_char; 8]>() as libc::c_ulong
                                        as isize,
                                )
                                .offset(-(1 as libc::c_int as isize)),
                        );
                    }
                }
                'r' => {
                    if strncmp(
                        *cur,
                        b"runas_gid=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 11]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                    {
                        runas_gid_str = (*cur)
                            .offset(
                                ::core::mem::size_of::<[libc::c_char; 11]>() as libc::c_ulong
                                    as isize,
                            )
                            .offset(-(1 as libc::c_int as isize));
                    } else if strncmp(
                        *cur,
                        b"runas_egid=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 12]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                    {
                        runas_egid_str = (*cur)
                            .offset(
                                ::core::mem::size_of::<[libc::c_char; 12]>() as libc::c_ulong
                                    as isize,
                            )
                            .offset(-(1 as libc::c_int as isize));
                    } else if strncmp(
                        *cur,
                        b"runas_uid=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 11]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                    {
                        runas_uid_str = (*cur)
                            .offset(
                                ::core::mem::size_of::<[libc::c_char; 11]>() as libc::c_ulong
                                    as isize,
                            )
                            .offset(-(1 as libc::c_int as isize));
                    } else if strncmp(
                        *cur,
                        b"runas_euid=\0" as *const u8 as *const libc::c_char,
                        (::core::mem::size_of::<[libc::c_char; 12]>() as libc::c_ulong)
                            .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                    ) == 0 as libc::c_int
                    {
                        runas_euid_str = (*cur)
                            .offset(
                                ::core::mem::size_of::<[libc::c_char; 12]>() as libc::c_ulong
                                    as isize,
                            )
                            .offset(-(1 as libc::c_int as isize));
                    }
                }
                _ => {}
            }
            cur = cur.offset(1);
        }

        /*
         * Lookup runas user and group, preferring effective over real uid/gid.
         */
        if !runas_euid_str.is_null() {
            runas_uid_str = runas_euid_str;
        }
        if !runas_uid_str.is_null() {
            id = sudo_strtoid_v2(runas_uid_str, &mut errstr);
            if !errstr.is_null() {
                sudo_warnx!(
                    b"runas uid %s: %s\0" as *const u8 as *const libc::c_char,
                    runas_uid_str,
                    errstr
                );
            } else {
                runas_uid = id;
            }
        }
        if !runas_egid_str.is_null() {
            runas_gid_str = runas_egid_str;
        }
        if !runas_gid_str.is_null() {
            id = sudo_strtoid_v2(runas_gid_str, &mut errstr);
            if !errstr.is_null() {
                sudo_warnx!(
                    b"runas gid %s: %s\0" as *const u8 as *const libc::c_char,
                    runas_gid_str,
                    errstr
                );
            } else {
                runas_gid = id;
            }
        }

        (*details).runas_pw = sudo_getpwuid(runas_uid);
        if ((*details).runas_pw).is_null() {
            idbuf[0_usize] = '#' as i32 as libc::c_char;
            sudo_strlcpy(
                &mut *idbuf.as_mut_ptr().offset(1_isize),
                runas_uid_str,
                (::core::mem::size_of::<[libc::c_char; 12]>() as libc::c_ulong)
                    .wrapping_sub(1 as libc::c_ulong),
            );
            (*details).runas_pw = sudo_fakepwnam(idbuf.as_mut_ptr(), runas_gid);
        }

        if runas_gid != (*(*details).runas_pw).pw_gid {
            (*details).runas_gr = sudo_getgrgid(runas_gid);
            if ((*details).runas_gr).is_null() {
                idbuf[0 as libc::c_int as usize] = '#' as i32 as libc::c_char;
                sudo_strlcpy(
                    &mut *idbuf.as_mut_ptr().offset(1 as libc::c_int as isize),
                    runas_gid_str,
                    (::core::mem::size_of::<[libc::c_char; 12]>() as libc::c_ulong)
                        .wrapping_sub(1 as libc::c_int as libc::c_ulong),
                );
                (*details).runas_gr = sudo_fakegrnam(idbuf.as_mut_ptr());
            }
        }

        debug_return_bool!(
            io_log_files[IOFD_STDIN as usize].enabled as libc::c_int != 0
                || io_log_files[IOFD_STDOUT as usize].enabled as libc::c_int != 0
                || io_log_files[IOFD_STDERR as usize].enabled as libc::c_int != 0
                || io_log_files[IOFD_TTYIN as usize].enabled as libc::c_int != 0
                || io_log_files[IOFD_TTYOUT as usize].enabled as libc::c_int != 0
        );
    } //unsafe
}

/*
 * Write the "/log" file that contains the user and command info.
 * This file is not compressed.
 */
fn write_info_log(
    mut pathbuf: *mut libc::c_char,
    mut len: size_t,
    mut details: *mut iolog_details,
    mut argv: *const *mut libc::c_char,
) -> bool {
    let mut now: time_t = 0;
    let mut av: *const *mut libc::c_char = std::ptr::null::<*mut libc::c_char>();
    let mut fp: *mut FILE = std::ptr::null_mut::<FILE>();
    let mut fd: libc::c_int = 0;
    let mut ret: bool = true;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_UTIL!());

        *pathbuf.offset(len as isize) = '\0' as i32 as libc::c_char;
        sudo_strlcat(
            pathbuf,
            b"/log\0" as *const u8 as *const libc::c_char,
            PATH_MAX as size_t,
        );
        fd = io_open(pathbuf, O_CREAT | O_TRUNC | O_WRONLY, iolog_filemode);
        if fd == -(1 as libc::c_int) || {
            fp = fdopen(fd, b"w\0" as *const u8 as *const libc::c_char);
            fp.is_null()
        } {
            log_warning(
                SLOG_SEND_MAIL,
                b"unable to create %s\0" as *const u8 as *const libc::c_char,
                pathbuf,
            );
            debug_return_bool!(false);
        }
        if fchown(fd, iolog_uid, iolog_gid) != 0 as libc::c_int {
            sudo_debug_printf!(
                SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO,
                b"%s: unable to fchown %d:%d %s\0" as *const u8 as *const libc::c_char,
                get_function_name!(),
                iolog_uid as libc::c_int,
                iolog_gid as libc::c_int,
                pathbuf
            );
        }

        fprintf(
            fp,
            b"%lld:%s:%s:%s:%s:%d:%d\n%s\n%s\0" as *const u8 as *const libc::c_char,
            time(&mut now) as libc::c_longlong,
            if !((*details).user).is_null() {
                (*details).user
            } else {
                b"unknown\0" as *const u8 as *const libc::c_char
            },
            (*(*details).runas_pw).pw_name,
            if !((*details).runas_gr).is_null() {
                (*(*details).runas_gr).gr_name as *const libc::c_char
            } else {
                b"\0" as *const u8 as *const libc::c_char
            },
            if !((*details).tty).is_null() {
                (*details).tty
            } else {
                b"unknown\0" as *const u8 as *const libc::c_char
            },
            (*details).lines,
            (*details).cols,
            if !((*details).cwd).is_null() {
                (*details).cwd
            } else {
                b"unknown\0" as *const u8 as *const libc::c_char
            },
            if !((*details).command).is_null() {
                (*details).command
            } else {
                b"unknown\0" as *const u8 as *const libc::c_char
            },
        );
        av = argv.offset(1_isize);
        while !(*av).is_null() {
            fputc(' ' as i32, fp);
            fputs(*av, fp);
            av = av.offset(1);
        }
        fputc('\n' as i32, fp);
        fflush(fp);
        if ferror(fp) != 0 {
            log_warning(
                SLOG_SEND_MAIL,
                b"unable to write to I/O log file: %s\0" as *const u8 as *const libc::c_char,
                strerror(*__errno_location()),
            );
            warned = true;
            ret = false;
        }
        fclose(fp);
        debug_return_bool!(ret);
    } //unsafe
}

unsafe extern "C" fn gzstrerror(mut file: gzFile) -> *const libc::c_char {
    let mut errnum: libc::c_int = 0;
    gzerror(file, &mut errnum)
}

/*
 * Write to an I/O log, compressing if iolog_compress is enabled.
 * If def_iolog_flush is true, flush the buffer immediately.
 */
fn iolog_write(
    mut ifd: io_fd,
    mut buf: *const libc::c_void,
    mut len: libc::c_uint,
) -> *const libc::c_char {
    let mut errstr: *const libc::c_char = std::ptr::null::<libc::c_char>();
    unsafe {
        debug_decl!(SUDOERS_DEBUG_PLUGIN!());

        'done: loop {
            if iolog_compress {
                if gzwrite(ifd.g, buf as voidp as voidpc, len) != len as libc::c_int {
                    errstr = gzstrerror(ifd.g);
                    break 'done;
                }
                if def_iolog_flush!() != 0 && gzflush(ifd.g, Z_SYNC_FLUSH) != Z_OK {
                    errstr = gzstrerror(ifd.g);
                    break 'done;
                }

                /*if def_iolog_flush!() != 0 {
                    if gzflush(ifd.g, Z_SYNC_FLUSH) != Z_OK {
                        errstr = gzstrerror(ifd.g);
                        break 'done;
                    }
                }*/
            } else if fwrite(buf, 1 as libc::c_ulong, len as libc::c_ulong, ifd.f)
                != len as libc::c_ulong
            {
                errstr = strerror(*__errno_location());
                break 'done;
            } else if def_iolog_flush!() != 0 && fflush(ifd.f) != 0 as libc::c_int {
                errstr = strerror(*__errno_location());
                break 'done;
            }

            /*} else if def_iolog_flush!() != 0 {
                if fflush(ifd.f) != 0 as libc::c_int {
                    errstr = strerror(*__errno_location());
                    break 'done;
                }
            }*/
            break;
        }
        debug_return_const_str!(errstr);
    } //unsafe
}

unsafe extern "C" fn sudoers_io_open(
    mut version: libc::c_uint,
    mut conversation: sudo_conv_t,
    mut plugin_printf: sudo_printf_t,
    mut settings: *const *mut libc::c_char,
    mut user_info: *const *mut libc::c_char,
    mut command_info: *const *mut libc::c_char,
    mut argc: libc::c_int,
    mut argv: *const *mut libc::c_char,
    mut user_env: *const *mut libc::c_char,
    mut args: *const *mut libc::c_char,
) -> libc::c_int {
    let mut debug_files: sudo_conf_debug_file_list = sudo_conf_debug_file_list {
        tqh_first: std::ptr::null_mut::<sudo_debug_file>(),
        tqh_last: std::ptr::null_mut::<*mut sudo_debug_file>(),
    };
    debug_files = {
        sudo_conf_debug_file_list {
            tqh_first: std::ptr::null_mut::<sudo_debug_file>(),
            tqh_last: &mut debug_files.tqh_first,
        }
        //init
    };
    let mut pathbuf: [libc::c_char; PATH_MAX] = [0; PATH_MAX];
    let mut sessid: [libc::c_char; 7] = [0; 7];
    let mut tofree: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut cur: *const *mut libc::c_char = std::ptr::null::<*mut libc::c_char>();
    let mut cp: *const libc::c_char = std::ptr::null::<libc::c_char>();
    let mut plugin_path: *const libc::c_char = std::ptr::null::<libc::c_char>();
    let mut len: size_t = 0;
    let mut omask: mode_t = 0;
    let mut i: libc::c_int = 0;
    let mut ret: libc::c_int = -(1 as libc::c_int);
    debug_decl!(SUDOERS_DEBUG_PLUGIN!());

    sudo_conv = conversation;
    sudo_printf = plugin_printf;

    /* If we have no command (because -V was specified) just return. */
    if argc == 0 {
        debug_return_int!(true as libc::c_int);
    }

    bindtextdomain(
        b"sudoers\0" as *const u8 as *const libc::c_char,
        b"/usr/share/locale\0" as *const u8 as *const libc::c_char,
    );

    /* Initialize the debug subsystem.  */
    cur = settings;
    loop {
        cp = *cur;
        if cp.is_null() {
            break;
        }
        if strncmp(
            cp,
            b"debug_flags=\0" as *const u8 as *const libc::c_char,
            (::core::mem::size_of::<[libc::c_char; 13]>() as libc::c_ulong)
                .wrapping_sub(1 as libc::c_int as libc::c_ulong),
        ) == 0 as libc::c_int
        {
            cp = cp.offset(
                (::core::mem::size_of::<[libc::c_char; 13]>() as libc::c_ulong)
                    .wrapping_sub(1 as libc::c_int as libc::c_ulong) as isize,
            );
            if !sudoers_debug_parse_flags(&mut debug_files, cp) {
                debug_return_int!(-(1 as libc::c_int));
            }
            if strncmp(
                cp,
                b"plugin_path=\0" as *const u8 as *const libc::c_char,
                (::core::mem::size_of::<[libc::c_char; 13]>() as libc::c_ulong)
                    .wrapping_sub(1 as libc::c_int as libc::c_ulong),
            ) == 0 as libc::c_int
            {
                plugin_path = cp
                    .offset(::core::mem::size_of::<[libc::c_char; 13]>() as libc::c_ulong as isize)
                    .offset(-(1 as libc::c_int as isize));
            }
        }
        cur = cur.offset(1);
    }

    /* umask must not be more restrictive than the file modes. */
    omask = umask(ACCESSPERMS!() as libc::c_uint & !(iolog_filemode | iolog_dirmode));

    'done: loop {
        if !sudoers_debug_register(plugin_path, &mut debug_files) {
            ret = -(1 as libc::c_int);
            break 'done;
        }

        /*
         * Pull iolog settings out of command_info.
         */
        if !iolog_deserialize_info(&mut iolog_details, user_info, command_info) {
            ret = false as libc::c_int;
            break 'done;
        }

        /* If no I/O log path defined we need to figure it out ourselves. */
        if (iolog_details.iolog_path).is_null() {
            /* Get next session ID and convert it into a path. */
            tofree = malloc(
                (::core::mem::size_of::<[libc::c_char; 17]>() as libc::c_ulong)
                    .wrapping_add(::core::mem::size_of::<[libc::c_char; 7]>() as libc::c_ulong)
                    .wrapping_add(2 as libc::c_int as libc::c_ulong),
            ) as *mut libc::c_char;
            if tofree.is_null() {
                sudo_warnx!(
                    b"%s: %s\0" as *const u8 as *const libc::c_char,
                    get_function_name!(),
                    b"unable to allocate memory\0" as *const u8 as *const libc::c_char,
                );
                break 'done;
            }
            memcpy(
                tofree as *mut libc::c_void,
                _PATH_SUDO_IO_LOGDIR!() as *const libc::c_void,
                ::core::mem::size_of::<[libc::c_char; 17]>() as libc::c_ulong,
            );
            if !io_nextid(
                tofree,
                std::ptr::null_mut::<libc::c_char>(),
                sessid.as_mut_ptr(),
            ) {
                ret = false as libc::c_int;
                break 'done;
            }
            snprintf(
                tofree
                    .offset(::core::mem::size_of::<[libc::c_char; 17]>() as libc::c_ulong as isize),
                (::core::mem::size_of::<[libc::c_char; 7]>() as libc::c_ulong)
                    .wrapping_add(2 as libc::c_ulong),
                b"%c%c/%c%c/%c%c\0" as *const u8 as *const libc::c_char,
                sessid[0_usize] as libc::c_int,
                sessid[1_usize] as libc::c_int,
                sessid[2_usize] as libc::c_int,
                sessid[3_usize] as libc::c_int,
                sessid[4_usize] as libc::c_int,
                sessid[5_usize] as libc::c_int,
            );
            iolog_details.iolog_path = tofree;
        }

        /*
         * Make local copy of I/O log path and create it, along with any
         * intermediate subdirs.  Calls mkdtemp() if iolog_path ends in XXXXXX.
         */
        len = mkdir_iopath(
            iolog_details.iolog_path,
            pathbuf.as_mut_ptr(),
            ::core::mem::size_of::<[libc::c_char; 4096]>() as libc::c_ulong,
        );
        if len >= ::core::mem::size_of::<[libc::c_char; 4096]>() as libc::c_ulong {
            break 'done;
        }

        /* Write log file with user and command details. */
        if !write_info_log(pathbuf.as_mut_ptr(), len, &mut iolog_details, argv) {
            break 'done;
        }

        /* Create the timing and I/O log files. */
        loop {
            if i >= IOFD_MAX {
                break;
            }
            if !open_io_fd(
                pathbuf.as_mut_ptr(),
                len,
                &mut *io_log_files.as_mut_ptr().offset(i as isize),
                iolog_compress,
            ) {
                break 'done;
            }
            i += 1;
        }

        /*
         * Clear I/O log function pointers for disabled log functions.
         */
        if !io_log_files[IOFD_STDIN as usize].enabled {
            sudoers_io.log_stdin = None;
        }
        if !io_log_files[IOFD_STDOUT as usize].enabled {
            sudoers_io.log_stdout = None;
        }
        if !io_log_files[IOFD_STDERR as usize].enabled {
            sudoers_io.log_stderr = None;
        }
        if !io_log_files[IOFD_TTYIN as usize].enabled {
            sudoers_io.log_ttyin = None;
        }
        if !io_log_files[IOFD_TTYOUT as usize].enabled {
            sudoers_io.log_ttyout = None;
        }

        if sudo_gettime_awake_v1(&mut last_time) == -(1 as libc::c_int) {
            sudo_debug_printf!(
                SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO,
                b"%s: unable to get time of day\0" as *const u8 as *const libc::c_char,
                get_function_name!()
            );
            break 'done;
        }
        ret = true as libc::c_int;
        break;
    }

    umask(omask);
    free(tofree as *mut libc::c_void);
    if !(iolog_details.runas_pw).is_null() {
        sudo_pw_delref(iolog_details.runas_pw);
    }
    if !(iolog_details.runas_gr).is_null() {
        sudo_gr_delref(iolog_details.runas_gr);
    }
    sudo_freepwcache();
    sudo_freegrcache();

    /* Ignore errors if they occur if the policy says so. */
    if ret == -(1 as libc::c_int) && iolog_details.ignore_iolog_errors as libc::c_int != 0 {
        ret = 0 as libc::c_int;
    }
    debug_return_int!(ret);
}

unsafe extern "C" fn sudoers_io_close(mut exit_status: libc::c_int, mut error: libc::c_int) {
    let mut errstr: *const libc::c_char = std::ptr::null::<libc::c_char>();
    let mut i: libc::c_int = 0;
    debug_decl!(SUDOERS_DEBUG_PLUGIN!());

    while i < IOFD_MAX {
        if !(io_log_files[i as usize].fd.v).is_null() {
            if iolog_compress {
                let mut errnum: libc::c_int = 0;

                if gzclose(io_log_files[i as usize].fd.g) != Z_OK {
                    errstr = gzerror(io_log_files[i as usize].fd.g, &mut errnum);
                }
            } else if fclose(io_log_files[i as usize].fd.f) != 0 as libc::c_int {
                errstr = strerror(*__errno_location());
            }
        }
        i += 1;
    }

    if !errstr.is_null() && !warned {
        /* Only warn about I/O log file errors once. */
        log_warning(
            SLOG_SEND_MAIL,
            b"unable to write to I/O log file: %s\0" as *const u8 as *const libc::c_char,
            errstr,
        );
        warned = true;
    }
    sudoers_debug_deregister();

    //return;
}

unsafe extern "C" fn sudoers_io_version(mut verbose: libc::c_int) -> libc::c_int {
    debug_decl!(SUDOERS_DEBUG_PLUGIN!());

    sudo_printf.expect("non-null function pointer")(
        SUDO_CONV_INFO_MSG,
        b"Sudoers I/O plugin version %s\n\0" as *const u8 as *const libc::c_char,
        PACKAGE_VERSION!(),
    );
    debug_return_int!(true as libc::c_int);
}

/*
 * Generic I/O logging function.  Called by the I/O logging entry points.
 * Returns 1 on success and -1 on error.
 */
unsafe extern "C" fn sudoers_io_log(
    mut ifd: io_fd,
    mut buf: *const libc::c_char,
    mut len: libc::c_uint,
    mut event: libc::c_int,
) -> libc::c_int {
    let mut now: timespec = timespec {
        tv_sec: 0,
        tv_nsec: 0,
    };
    let mut delay: timespec = timespec {
        tv_sec: 0,
        tv_nsec: 0,
    };
    let mut tbuf: [libc::c_char; 1024] = [0; 1024];
    let mut errstr: *const libc::c_char = std::ptr::null::<libc::c_char>();
    let mut ret: libc::c_int = -(1 as libc::c_int);
    debug_decl!(SUDOERS_DEBUG_PLUGIN!());

    if (ifd.v).is_null() {
        sudo_warnx!(
            b"%s: internal error, I/O log file for event %d not open\0" as *const u8
                as *const libc::c_char,
            get_function_name!(),
            event
        );
        debug_return_int!(-(1 as libc::c_int));
    }

    'bad: loop {
        if sudo_gettime_awake_v1(&mut now) == -(1 as libc::c_int) {
            sudo_debug_printf!(
                SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO,
                b"%s: unable to get time of day\0" as *const u8 as *const libc::c_char,
                get_function_name!()
            );
            errstr = strerror(*__errno_location());
            break 'bad;
        }

        'done: loop {
            /* Write I/O log file entry. */
            errstr = iolog_write(ifd, buf as *const libc::c_void, len);
            if !errstr.is_null() {
                break 'done;
            }

            /* 1. 计算事件间隔 */
            delay.tv_sec = now.tv_sec - last_time.tv_sec;
            delay.tv_nsec = now.tv_nsec - last_time.tv_nsec;
            if delay.tv_nsec < 0 {
                delay.tv_sec -= 1;
                delay.tv_nsec += 1_000_000_000;
            }

            /* 2. 写入 timing 文件 */
            let data_len = len;
            let timing_str_len = snprintf(
                tbuf.as_mut_ptr(),
                ::core::mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong,
                b"%d %lld.%09ld %u\n\0" as *const u8 as *const libc::c_char,
                event as libc::c_int,
                delay.tv_sec as libc::c_longlong,
                delay.tv_nsec as libc::c_long,
                data_len as libc::c_uint,
            ) as libc::c_uint;
            if timing_str_len as libc::c_ulong
                >= ::core::mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong
            {
                /* Not actually possible due to the size of tbuf[]. */
                errstr = strerror(EOVERFLOW);
                break 'done;
            }
            errstr = iolog_write(
                io_log_files[IOFD_TIMING as usize].fd,
                tbuf.as_mut_ptr() as *const libc::c_void,
                timing_str_len,
            );
            if !errstr.is_null() {
                break 'done;
            }

            /* 3. 更新 last_time 为当前事件时间 */
            last_time.tv_sec = now.tv_sec;
            last_time.tv_nsec = now.tv_nsec;

            /* Success. */
            ret = 1;
            break;
        }
        break;
    }
    if ret == -(1 as libc::c_int) {
        if !errstr.is_null() && !warned {
            log_warning(
                SLOG_SEND_MAIL,
                b"unable to write to I/O log file: %s\0" as *const u8 as *const libc::c_char,
                errstr,
            );
            warned = true;
        }
        if iolog_details.ignore_iolog_errors {
            ret = 1;
        }
    }
    debug_return_int!(ret);
}

unsafe extern "C" fn sudoers_io_log_stdin(
    mut buf: *const libc::c_char,
    mut len: libc::c_uint,
) -> libc::c_int {
    let ifd: io_fd = io_log_files[IOFD_STDIN as usize].fd;
    sudoers_io_log(ifd, buf, len, IO_EVENT_STDIN)
}

unsafe extern "C" fn sudoers_io_log_stdout(
    mut buf: *const libc::c_char,
    mut len: libc::c_uint,
) -> libc::c_int {
    let ifd: io_fd = io_log_files[IOFD_STDOUT as usize].fd;
    sudoers_io_log(ifd, buf, len, IO_EVENT_STDOUT)
}

unsafe extern "C" fn sudoers_io_log_stderr(
    mut buf: *const libc::c_char,
    mut len: libc::c_uint,
) -> libc::c_int {
    let ifd: io_fd = io_log_files[IOFD_STDERR as usize].fd;
    sudoers_io_log(ifd, buf, len, IO_EVENT_STDERR)
}

unsafe extern "C" fn sudoers_io_log_ttyin(
    mut buf: *const libc::c_char,
    mut len: libc::c_uint,
) -> libc::c_int {
    let ifd: io_fd = io_log_files[IOFD_TTYIN as usize].fd;
    sudoers_io_log(ifd, buf, len, IO_EVENT_TTYIN)
}

unsafe extern "C" fn sudoers_io_log_ttyout(
    mut buf: *const libc::c_char,
    mut len: libc::c_uint,
) -> libc::c_int {
    let ifd: io_fd = io_log_files[IOFD_TTYOUT as usize].fd;
    sudoers_io_log(ifd, buf, len, IO_EVENT_TTYOUT)
}

unsafe extern "C" fn sudoers_io_change_winsize(
    mut lines: libc::c_uint,
    mut cols: libc::c_uint,
) -> libc::c_int {
    let mut now: timespec = timespec {
        tv_sec: 0,
        tv_nsec: 0,
    };
    let mut delay: timespec = timespec {
        tv_sec: 0,
        tv_nsec: 0,
    };
    let mut len: libc::c_uint = 0;
    let mut tbuf: [libc::c_char; 1024] = [0; 1024];
    let mut errstr: *const libc::c_char = std::ptr::null::<libc::c_char>();
    let mut ret: libc::c_int = -(1 as libc::c_int);
    debug_decl!(SUDOERS_DEBUG_PLUGIN!());

    'bad: loop {
        if sudo_gettime_awake_v1(&mut now) == -(1 as libc::c_int) {
            sudo_debug_printf!(
                SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO,
                b"%s: unable to get time of day\0" as *const u8 as *const libc::c_char,
                get_function_name!()
            );
            errstr = strerror(*__errno_location());
            break 'bad;
        }
        'done: loop {
            /* Write window change event to the timing file. */
            sudo_timespecsub!(now, last_time, delay);
            len = snprintf(
                tbuf.as_mut_ptr(),
                ::core::mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong,
                b"%d %lld.%09ld %u %u\n\0" as *const u8 as *const libc::c_char,
                IO_EVENT_WINSIZE,
                delay.tv_sec as libc::c_longlong,
                delay.tv_nsec,
                lines,
                cols,
            ) as libc::c_uint;
            if len as libc::c_ulong
                >= ::core::mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong
            {
                /* Not actually possible due to the size of tbuf[]. */
                errstr = strerror(EOVERFLOW);
                break 'done;
            }
            errstr = iolog_write(
                io_log_files[IOFD_TIMING as usize].fd,
                tbuf.as_mut_ptr() as *const libc::c_void,
                len,
            );
            if !errstr.is_null() {
                break 'done;
            }

            /* Success. */
            ret = 1;
            break;
        }
        //done:
        last_time.tv_sec = now.tv_sec;
        last_time.tv_nsec = now.tv_nsec;
        break;
    }
    //bad:
    if ret == -(1 as libc::c_int) {
        if !errstr.is_null() && !warned {
            /* Only warn about I/O log file errors once. */
            log_warning(
                SLOG_SEND_MAIL,
                b"unable to write to I/O log file: %s\0" as *const u8 as *const libc::c_char,
                errstr,
            );
            warned = true;
        }
        /* Ignore errors if they occur if the policy says so. */
        if iolog_details.ignore_iolog_errors {
            ret = 1;
        }
    }

    debug_return_int!(ret);
}

unsafe extern "C" fn sudoers_io_suspend(mut signo: libc::c_int) -> libc::c_int {
    let mut now: timespec = timespec {
        tv_sec: 0,
        tv_nsec: 0,
    };
    let mut delay: timespec = timespec {
        tv_sec: 0,
        tv_nsec: 0,
    };
    let mut len: libc::c_uint = 0;
    let mut signame: [libc::c_char; SIG2STR_MAX!()] = [0; SIG2STR_MAX!()];
    let mut tbuf: [libc::c_char; 1024] = [0; 1024];
    let mut errstr: *const libc::c_char = std::ptr::null::<libc::c_char>();
    let mut ret: libc::c_int = -(1 as libc::c_int);
    debug_decl!(SUDOERS_DEBUG_PLUGIN!());

    if signo <= 0 as libc::c_int || sudo_sig2str(signo, signame.as_mut_ptr()) == -(1 as libc::c_int)
    {
        sudo_warnx!(
            b"%s: internal error, invalid signal %d\0" as *const u8 as *const libc::c_char,
            get_function_name!(),
            signo
        );
        debug_return_int!(-(1 as libc::c_int));
    }

    'bad: loop {
        if sudo_gettime_awake_v1(&mut now) == -(1 as libc::c_int) {
            sudo_debug_printf!(
                SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO,
                b"%s: unable to get time of day\0" as *const u8 as *const libc::c_char,
                get_function_name!()
            );
            errstr = strerror(*__errno_location());
            break 'bad;
        }

        'done: loop {
            /* Write suspend event to the timing file. */
            sudo_timespecsub!(now, last_time, delay);
            len = snprintf(
                tbuf.as_mut_ptr(),
                ::core::mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong,
                b"%d %lld.%09ld %s\n\0" as *const u8 as *const libc::c_char,
                IO_EVENT_SUSPEND,
                delay.tv_sec as libc::c_longlong,
                delay.tv_nsec,
                signame.as_mut_ptr(),
            ) as libc::c_uint;
            if len as libc::c_ulong
                >= ::core::mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong
            {
                /* Not actually possible due to the size of tbuf[]. */
                errstr = strerror(EOVERFLOW);
                break 'done;
            }
            errstr = iolog_write(
                io_log_files[IOFD_TIMING as usize].fd,
                tbuf.as_mut_ptr() as *const libc::c_void,
                len,
            );
            if !errstr.is_null() {
                break 'done;
            }

            /* Success. */
            ret = 1;
            break;
        }
        //done：
        last_time.tv_sec = now.tv_sec;
        last_time.tv_nsec = now.tv_nsec;
        break;
    }

    //bad
    if ret == -(1 as libc::c_int) {
        if !errstr.is_null() && !warned {
            /* Only warn about I/O log file errors once. */
            log_warning(
                SLOG_SEND_MAIL,
                b"unable to write to I/O log file: %s\0" as *const u8 as *const libc::c_char,
                errstr,
            );
            warned = true;
        }
        /* Ignore errors if they occur if the policy says so. */
        if iolog_details.ignore_iolog_errors {
            ret = 1;
        }
    }
    debug_return_int!(ret);
}

#[no_mangle]
pub static mut sudoers_io: io_plugin = unsafe {
    {
        io_plugin {
            type_0: SUDO_IO_PLUGIN as libc::c_uint,
            version: SUDO_API_VERSION!() as libc::c_uint,
            open: Some(
                sudoers_io_open
                    as unsafe extern "C" fn(
                        libc::c_uint,
                        sudo_conv_t,
                        sudo_printf_t,
                        *const *mut libc::c_char,
                        *const *mut libc::c_char,
                        *const *mut libc::c_char,
                        libc::c_int,
                        *const *mut libc::c_char,
                        *const *mut libc::c_char,
                        *const *mut libc::c_char,
                    ) -> libc::c_int,
            ),
            close: Some(sudoers_io_close as unsafe extern "C" fn(libc::c_int, libc::c_int) -> ()),
            show_version: Some(
                sudoers_io_version as unsafe extern "C" fn(libc::c_int) -> libc::c_int,
            ),
            log_ttyin: Some(
                sudoers_io_log_ttyin
                    as unsafe extern "C" fn(*const libc::c_char, libc::c_uint) -> libc::c_int,
            ),
            log_ttyout: Some(
                sudoers_io_log_ttyout
                    as unsafe extern "C" fn(*const libc::c_char, libc::c_uint) -> libc::c_int,
            ),
            log_stdin: Some(
                sudoers_io_log_stdin
                    as unsafe extern "C" fn(*const libc::c_char, libc::c_uint) -> libc::c_int,
            ),
            log_stdout: Some(
                sudoers_io_log_stdout
                    as unsafe extern "C" fn(*const libc::c_char, libc::c_uint) -> libc::c_int,
            ),
            log_stderr: Some(
                sudoers_io_log_stderr
                    as unsafe extern "C" fn(*const libc::c_char, libc::c_uint) -> libc::c_int,
            ),
            register_hooks: None,   /* register_hooks */
            deregister_hooks: None, /* deregister_hooks */
            change_winsize: Some(
                sudoers_io_change_winsize
                    as unsafe extern "C" fn(libc::c_uint, libc::c_uint) -> libc::c_int,
            ),
            log_suspend: Some(
                sudoers_io_suspend as unsafe extern "C" fn(libc::c_int) -> libc::c_int,
            ),
        }
        //init
    }
};
